﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using Microsoft.Office.Core;
using Microsoft.Office.Interop.PowerPoint;
using Microsoft.Office.PowerPoint.STAMP.Properties;
using Shape = Microsoft.Office.Interop.PowerPoint.Shape;

namespace Microsoft.Office.PowerPoint.STAMP.Core
{
    /// <summary>
    /// Main class for managing captions
    /// </summary>
    public class CaptionManager
    {
        /// <summary>
        /// Contains a stored / cache value if the active shape has captions (a caption tag)
        /// </summary>
        private static bool _activeShapeHasCaptions;

        private static bool _mediaCaptionsHidden;

        private readonly string _mediaName;
        private readonly Shape _mediaShape;
        private readonly Slide _slide;

        /// <summary>
        /// List of validation errors (following validation failure)
        /// </summary>
        private readonly List<string> _validationErrors = new List<string>();

        private string _captionId;
        private CaptionItemsList _captionItemsList;

        /// <summary>
        /// Caption Manager contructor for setting up the 
        /// </summary>
        /// <param name="slide"></param>
        /// <param name="mediaShape"></param>
        public CaptionManager(Slide slide, Shape mediaShape)
        {
            _slide = slide;
            _mediaShape = mediaShape;
            _mediaName = Utils.GetShapeName(_mediaShape.Name);

            if (HasSavedCaptions)
            {
                _captionId = _mediaShape.Tags[Constants.CaptionMediaId];
                RetreiveCaptions(_mediaShape.Tags[Constants.CustomXMLPartId]);
            }
        }

        /// <summary>
        /// List of captions loaded in the Manager
        /// </summary>
        public CaptionItemsList CaptionItemsList
        {
            get { return _captionItemsList; }
        }

        /// <summary>
        /// Checks if the shape has any saved captions.
        /// </summary>
        public bool HasSavedCaptions
        {
            get { return !String.IsNullOrEmpty(_mediaShape.Tags[Constants.CaptionMediaId]); }
        }

        private string CaptionId
        {
            get
            {
                if (String.IsNullOrEmpty(_captionId))
                    _captionId = Guid.NewGuid().ToString();

                return _captionId;
            }
        }

        /// <summary>
        /// Gets list of validations errors.
        /// </summary>
        public List<string> ValidationErrors
        {
            get { return _validationErrors; }
        }

        /// <summary>
        /// Returns a list of caption shapes
        /// </summary>
        public IList<Shape> CaptionShapes
        {
            get { return ShapeFinder.FindShapesByMediaId(_slide, _captionId); }
        }

        /// <summary>
        /// Updates the 'cached' value of if the active shape has captions.  Implemented to
        /// remove redundant checks in the ActiveShapeHasCaptions method.
        /// </summary>
        public static void CheckShapeForCaptionsAfterChange()
        {
            var application = Globals.ThisAddIn.Application;
            Slide activeSlide = application.ActiveWindow.View.Slide;
            Shape shape = application.ActiveWindow.Selection.ShapeRange[1];

            _activeShapeHasCaptions = !String.IsNullOrEmpty(shape.Tags[Constants.CaptionMediaId]);
            _mediaCaptionsHidden = !String.IsNullOrEmpty(shape.Tags[Constants.MediaCaptionsHidden])
                                       ? Boolean.Parse(shape.Tags[Constants.MediaCaptionsHidden])
                                       : false;
        }

        /// <summary>
        /// Property that checks the current selection to see if there are captions already created
        /// for it.
        /// </summary>
        /// <remarks>Note this returns the cached value from <see cref="UpdateActiveShapeChange"/></remarks>
        /// <returns>True is the active shape has captions.</returns>
        public static bool ActiveShapeHasCaptions()
        {
            return _activeShapeHasCaptions;
        }

        /// <summary>
        /// Property that checks the current selection to see if there are captions already created
        /// for it.
        /// </summary>
        /// <remarks>Note this returns the cached value from <see cref="UpdateActiveShapeChange"/></remarks>
        /// <returns>True is the active shape has captions.</returns>
        public static bool MediaCaptionsHidden()
        {
            return _mediaCaptionsHidden;
        }

        /// <summary>
        /// Main method for rendering new and updating dirty captions on the slide
        /// </summary>
        /// <param name="captionItemsList">List of new and/or dirty captions</param>
        public void RenderClosedCaptions(CaptionItemsList captionItemsList)
        {
            _captionItemsList = captionItemsList;

            if (_captionItemsList == null || _captionItemsList.CaptionItems == null || _captionItemsList.CaptionItems.Count == 0)
            {
                DeleteCaptionIdTag();
                return;
            }

            // Building list of new captions to create on the slide
            IEnumerable<CaptionItem> newCaptions = captionItemsList.CaptionItems.Where(a => a.IsNew);
            foreach (CaptionItem newCaption in newCaptions)
            {
                AddBookMarks(newCaption);
                CreateCaptionShapes(newCaption);
            }

            // Build list of dirty captions, then update the captions on the slide
            IEnumerable<CaptionItem> dirtyCaptions = captionItemsList.CaptionItems.Where(a => a.IsDirty && !a.IsNew);
            foreach (CaptionItem dirtyCaption in dirtyCaptions)
            {
                UpdateDirtyCaptions(dirtyCaption);
            }

            // Add the Caption display area to the slide 
            SetDisplayAreaAndVisibilitySettings();
        }

        /// <summary>
        /// Ensures that caption display area is visible and hides any shapes if the current captions are hidden.
        /// </summary>
        public void SetDisplayAreaAndVisibilitySettings()
        {
            // CaptionDisplayArea();
            CheckCaptionVisibility();
        }

        public void RenderSingleSetup(CaptionItemsList captionItemsList)
        {
            _captionItemsList = captionItemsList;

            if (_captionItemsList == null || _captionItemsList.CaptionItems == null || _captionItemsList.CaptionItems.Count == 0)
            {
                DeleteCaptionIdTag();
                return;
            }
        }

        public void RenderSingleCaptionItem(CaptionItem captionItem)
        {
            if (captionItem.IsNew)
            {
                AddBookMarks(captionItem);
                CreateCaptionShapes(captionItem);
            }
            if (captionItem.IsDirty && !captionItem.IsNew)
            {
                UpdateDirtyCaptions(captionItem);
            }
        }

        /// <summary>
        /// Determines if the captions are valid or not
        /// Creates a list of validation errors when appropriate
        /// </summary>
        /// <param name="captionItemsList">List of captions to validate.</param>
        /// <returns></returns>
        public bool Validate(CaptionItemsList captionItemsList)
        {
            _validationErrors.Clear();
            _captionItemsList = captionItemsList;

            var mediaEndPoint = _mediaShape.MediaFormat.EndPoint;

            foreach (CaptionItem caption in _captionItemsList.CaptionItems)
            {
                #region check for duplicate bookmarks
                var itemFoundBookMarkEntryPosition =
                    _captionItemsList.CaptionItems.Any(
                        a =>
                        a.InternalId != caption.InternalId &&
                        (a.BookMarkEntryPosition.Equals(caption.BookMarkEntryPosition) ||
                         a.BookMarkExitPosition.Equals(caption.BookMarkEntryPosition)));

                var itemFoundBookMarkExitPosition =
                    _captionItemsList.CaptionItems.Any(
                        a =>
                        a.InternalId != caption.InternalId &&
                        (a.BookMarkExitPosition.Equals(caption.BookMarkExitPosition) ||
                         a.BookMarkEntryPosition.Equals(caption.BookMarkExitPosition)));

                if (caption.BookMarkEntryPosition.Equals(caption.BookMarkExitPosition))
                {
                    string validationMessage = String.Format(Resources.CaptionManager_Validate_DuplicateStartEndTime,
                                                        caption.BookMarkEntryPosition.ToTimeSpanString(Resources.TimeSpanStringFormat),
                                                        Utils.FirstThreeWords(caption.Text)
                                                        , caption.BookMarkExitPosition.ToTimeSpanString(Resources.TimeSpanStringFormat));
                    _validationErrors.Add(validationMessage);
                }

                if (itemFoundBookMarkEntryPosition)
                {
                    string validationMessage = String.Format(Resources.CaptionManager_Validate_DuplicateStartTime,
                                                        caption.BookMarkEntryPosition.ToTimeSpanString(Resources.TimeSpanStringFormat),
                                                        Utils.FirstThreeWords(caption.Text));
                    _validationErrors.Add(validationMessage);
                }
                if (itemFoundBookMarkExitPosition)
                {
                    string validationMessage = String.Format(Resources.CaptionManager_Validate_DuplicateEndTime,
                                                        caption.BookMarkExitPosition.ToTimeSpanString(Resources.TimeSpanStringFormat),
                                                        Utils.FirstThreeWords(caption.Text));
                    _validationErrors.Add(validationMessage);
                }
                #endregion

                #region check end time to media length

                if (caption.BookMarkEntryPosition > mediaEndPoint)
                {
                    string validationMessage = String.Format(Resources.CaptionManager_Validate_StartTime_MediaLength,
                                                        caption.BookMarkExitPosition.ToTimeSpanString(Resources.TimeSpanStringFormat),
                                                        mediaEndPoint.ToTimeSpanString(Resources.TimeSpanStringFormat));
                    _validationErrors.Add(validationMessage);
                }

                if (caption.BookMarkExitPosition > mediaEndPoint)
                {
                    string validationMessage = String.Format(Resources.CaptionManager_Validate_EndTime_MediaLength,
                                                       caption.BookMarkExitPosition.ToTimeSpanString(Resources.TimeSpanStringFormat),
                                                        mediaEndPoint.ToTimeSpanString(Resources.TimeSpanStringFormat));
                    _validationErrors.Add(validationMessage);
                }

                if (caption.BookMarkEntryPosition > caption.BookMarkExitPosition)
                {

                    string validationMessage = String.Format(Resources.CaptionManager_Validate_StartTime_Greater_EndTime,
                                                       caption.BookMarkEntryPosition.ToTimeSpanString(Resources.TimeSpanStringFormat),
                                                       caption.BookMarkExitPosition.ToTimeSpanString(Resources.TimeSpanStringFormat));
                    _validationErrors.Add(validationMessage);
                }

                if (String.IsNullOrEmpty(caption.Text))
                {
                    string validationMessage = Resources.CaptionManager_Validate_Text_Empty;
                    _validationErrors.Add(validationMessage);
                }

                #endregion
            }

            return _validationErrors.Count == 0;
        }


        private void CheckCaptionVisibility()
        {
            if (String.IsNullOrEmpty(_mediaShape.Tags[Constants.MediaCaptionsHidden]))
            {
                UtilPowerPoint.SetShapeTag(_mediaShape, Constants.MediaCaptionsHidden, false.ToString());
            }
        }

        /// <summary>
        /// Sets the Captions dispaly area text and brings it to the front
        /// BUG: does not hide correctly when there are other animations in the slide animation sequence.
        /// </summary>
        public void CaptionDisplayArea()
        {
            string text = Resources.CaptionManager_CaptionDisplayArea_Text;
            string captionName = Utils.CreateShapeName(_mediaName, text);
            Shape shapeCaptionDisplayArea = FindShapesByName(captionName);

            if (shapeCaptionDisplayArea == null)
            {
                CaptionStyle captionStyle = new CaptionStyle();
                captionStyle.CaptionDisplayArea = true;
                RectangleF position = Utils.GetShapePosition(_slide, _mediaShape, captionStyle, text);

                Shape textbox = UtilPowerPoint.AddTextboxShape(_slide, position.X, position.Y, position.Width,
                                                               position.Height);

                textbox.TextFrame.AutoSize = PpAutoSize.ppAutoSizeNone;

                CaptionItem caption = new CaptionItem
                                      {
                                          Name = captionName,
                                          Text = text,
                                          InternalId = Guid.NewGuid().ToString(),
                                          IsCaptionDisplayArea = true
                                      };
                UtilPowerPoint.SetTextboxCaptionDetails(textbox, caption, CaptionId, caption.Text);

                StyleCaptionShape(textbox, captionStyle);

                textbox.ZOrder(MsoZOrderCmd.msoBringToFront);

                Effect effect = _slide.TimeLine.MainSequence.AddEffect(textbox, MsoAnimEffect.msoAnimEffectAppear,
                                                                       MsoAnimateByLevel.msoAnimateLevelNone,
                                                                       MsoAnimTriggerType.msoAnimTriggerAfterPrevious,
                                                                       -1);
                effect.Timing.TriggerDelayTime = 0.0f;
                effect.Exit = MsoTriState.msoTrue;
            }
            else
            {
                shapeCaptionDisplayArea.ZOrder(MsoZOrderCmd.msoBringToFront);
            }
        }

        /// <summary>
        /// Saves the Caption data as an XML string resource in the presentation
        /// </summary>
        public void SaveCaptions()
        {
            if (_captionItemsList != null && _captionItemsList.CaptionItems != null && _captionItemsList.CaptionItems.Count > 0)
            {
                _captionItemsList.Id = CaptionId;

                // Removes the previously stored XML resource
                if (!String.IsNullOrEmpty(_mediaShape.Tags[Constants.CustomXMLPartId]))
                {
                    XmlCleanup(_mediaShape.Tags[Constants.CustomXMLPartId]);
                }

                string xmlString = UtilXmlToString.SerializeObject(CaptionItemsList);
                string customXmlPartId =
                    UtilPowerPoint.AddCustomXmlPartToPresentation(_slide.Application.ActivePresentation, xmlString);

                UtilPowerPoint.SetShapeTag(_mediaShape, Constants.CustomXMLPartId, customXmlPartId);
                UtilPowerPoint.SetShapeTag(_mediaShape, Constants.CaptionMediaId, CaptionId);
            }
            else if (_captionItemsList != null && _captionItemsList.CaptionItems != null && _captionItemsList.CaptionItems.Count == 0)
            {
                RemoveSlideCaptions();
            }
        }

        /// <summary>
        /// Retreives the Captions saved in the presentation
        /// </summary>
        /// <param name="customXmlPartId"></param>
        /// <returns></returns>
        public CaptionItemsList RetreiveCaptions(string customXmlPartId)
        {
            string s = UtilPowerPoint.GetCustomXmlPartToPresentationById(_slide.Application.ActivePresentation, customXmlPartId);

            _captionItemsList = UtilXmlToString.DeserializeObject<CaptionItemsList>(s);

            return _captionItemsList;
        }

        /// <summary>
        /// Creates the captions shapes based on the caption details.
        /// </summary>
        /// <param name="caption">Caption item.</param>
        public void CreateCaptionShapes(CaptionItem caption)
        {
            RectangleF position = Utils.GetShapePosition(_slide, _mediaShape, caption.CaptionStyle, caption.Text);

            Shape textbox = UtilPowerPoint.AddTextboxShape(_slide, position.X, position.Y, position.Width, position.Height);

            UtilPowerPoint.SetTextboxCaptionDetails(textbox, caption, CaptionId, caption.Text);

            StyleCaptionShape(textbox, caption.CaptionStyle);

            ApplyAnimation(textbox, caption);
            
            var boundHeight = textbox.TextFrame.TextRange.BoundHeight;
            if (boundHeight > position.Height)
            {
                RectangleF mediaRectangle = new RectangleF(_mediaShape.Left, _mediaShape.Top, _mediaShape.Width, _mediaShape.Height);
                RectangleF slideRectangle = new RectangleF(0, 0, _slide.Application.ActivePresentation.PageSetup.SlideWidth, _slide.Application.ActivePresentation.PageSetup.SlideHeight);

                var width = Utils.DetermineShapeWidth(caption.CaptionStyle.CaptionLocation, slideRectangle.Width, mediaRectangle.Width);
                SizeF textSize = Utils.GetTextboxShapeSizeF(_slide, caption.Text, caption.CaptionStyle, width);

                var numberLines = Math.Ceiling(boundHeight/textSize.Height);

                if (numberLines <= caption.CaptionStyle.CaptionLines)
                {
                    var loc = Utils.DetermineShapeLocation(slideRectangle, mediaRectangle,
                                                           Utils.GetCaptionLocation(_mediaShape),
                                                           new SizeF(position.Width, boundHeight));
                    textbox.Left = loc.X;
                    textbox.Top = loc.Y;
                    textbox.Height = boundHeight;
                }
            }
        }

        /// <summary>
        /// Applying the fade in and fade out effect to the textbox
        /// </summary>
        /// <param name="textbox"></param>
        /// <param name="caption"></param>
        private void ApplyAnimation(Shape textbox, CaptionItem caption)
        {
            UtilPowerPoint.AddEntranceAnimation(_slide, textbox, _mediaShape, caption.BookMarkEntryName);
            UtilPowerPoint.AddExitAnimation(_slide, textbox, _mediaShape, caption.BookMarkExitName);
        }

        private void StyleCaptionShape(Shape shape, CaptionStyle captionStyle)
        {
            shape.TextFrame.TextRange.Font.Bold = captionStyle.FontWeight == FontStyle.Bold
                                                      ? MsoTriState.msoTrue
                                                      : MsoTriState.msoFalse;
            shape.TextFrame.TextRange.Font.Italic = captionStyle.FontStyle == FontStyle.Italic
                                                        ? MsoTriState.msoTrue
                                                        : MsoTriState.msoFalse;
            shape.TextFrame.TextRange.Font.Underline = captionStyle.TextDecorationStyleProperty ==
                                                       TextDecorationAttributeValue.underline
                                                           ? MsoTriState.msoTrue
                                                           : MsoTriState.msoFalse;
            switch (captionStyle.TextAlignStyleProperty)
            {
                case TextAlignAttibuteValue.start:
                case TextAlignAttibuteValue.left:
                    shape.TextFrame.TextRange.ParagraphFormat.Alignment = PpParagraphAlignment.ppAlignLeft;
                    break;
                case TextAlignAttibuteValue.center:
                    shape.TextFrame.TextRange.ParagraphFormat.Alignment = PpParagraphAlignment.ppAlignCenter;
                    break;
                case TextAlignAttibuteValue.right:
                case TextAlignAttibuteValue.end:
                    shape.TextFrame.TextRange.ParagraphFormat.Alignment = PpParagraphAlignment.ppAlignRight;
                    break;
            }


            shape.TextFrame.TextRange.Font.Size = (float)captionStyle.FontSize.ToPixelLength();
            shape.TextFrame.TextRange.Font.Name = captionStyle.FontFamily.Name;

            int fontColorRgb = ((captionStyle.FontColor.B * 256) + captionStyle.FontColor.G) * 256 +
                               captionStyle.FontColor.R;
            int backColorRgb = ((captionStyle.BackgroundColor.B * 256) + captionStyle.BackgroundColor.G) * 256 +
                               captionStyle.BackgroundColor.R;

            shape.TextFrame.TextRange.Font.Color.RGB = fontColorRgb;
            shape.Fill.BackColor.RGB = backColorRgb;
            // shape.Fill.Transparency = Convert.ToInt64(captionStyle.Opacity);

            shape.TextFrame.MarginBottom = 0;
            shape.TextFrame.MarginTop = 0;

            if (!String.IsNullOrEmpty(_mediaShape.Tags[Constants.MediaCaptionsHidden]))
            {
                shape.Visible = Boolean.Parse(_mediaShape.Tags[Constants.MediaCaptionsHidden])
                                    ? MsoTriState.msoFalse
                                    : MsoTriState.msoTrue;
            }
        }

        /// <summary>
        /// Add the Bookmarks to the media object using the caption item
        /// </summary>
        /// <param name="caption"></param>
        private void AddBookMarks(CaptionItem caption)
        {
            UtilPowerPoint.AddMediaBookmark(_mediaShape, caption.BookMarkEntryPosition, caption.BookMarkEntryName);
            UtilPowerPoint.AddMediaBookmark(_mediaShape, caption.BookMarkExitPosition, caption.BookMarkExitName);
        }


        /// <summary>
        /// Sets the visibility of all the captions shapes of the media
        /// </summary>
        public void HideCaptions()
        {
            IList<Shape> captionShapes = CaptionShapes;
            foreach (Shape shape in captionShapes)
            {
                shape.Visible = MsoTriState.msoFalse;
            }

            UtilPowerPoint.SetShapeTag(_mediaShape, Constants.MediaCaptionsHidden, true.ToString());
        }

        /// <summary>
        /// Sets the visibility of all the captions shapes of the media
        /// </summary>
        public void ShowCaptions()
        {
            IList<Shape> captionShapes = CaptionShapes;
            foreach (Shape shape in captionShapes)
            {
                shape.Visible = MsoTriState.msoTrue;
            }

            UtilPowerPoint.SetShapeTag(_mediaShape, Constants.MediaCaptionsHidden, false.ToString());
        }

        /// <summary>
        /// Remove captions and all associated elements ie animations, bookmarks and shapes
        /// </summary>
        public void RemoveSlideCaptions()
        {
            DeleteAnimations();
            DeleteBookmarks();
            DeleteShapes();

            if (HasSavedCaptions)
                DeleteCaptionIdTag();
        }

        /// <summary>
        /// Removes the stored XML from the presentation
        /// </summary>
        /// <param name="id">Id of the stored XML to be removed</param>
        private void XmlCleanup(string id)
        {
            if (!String.IsNullOrEmpty(id))
                UtilPowerPoint.DeleteCustomXmlPartToPresentationById(_slide.Application.ActivePresentation, id);
        }

        /// <summary>
        /// Deletes all references to the stored Captions
        /// </summary>
        public void DeleteCaptionIdTag()
        {
            XmlCleanup(_mediaShape.Tags[Constants.CustomXMLPartId]);
            _mediaShape.Tags.Delete(Constants.CaptionMediaId);
            _mediaShape.Tags.Delete(Constants.CustomXMLPartId);
            _mediaShape.Tags.Delete(Constants.MediaCaptionsHidden);
        }

        /// <summary>
        /// Loops throught each shape and deletes the animations
        /// </summary>
        public void DeleteAnimations()
        {
            IList<string> shapeNames = CaptionShapes.Select(a => a.Name).ToList();

            UtilPowerPoint.DeleteShapeAnimationsByTriggerType(_slide, null, MsoAnimTriggerType.msoAnimTriggerOnMediaBookmark, shapeNames);
        }

        /// <summary>
        /// Loops throught the bookmarks and deletes the bookmarks where the bookmark name matches the media name
        /// </summary>
        public void DeleteBookmarks()
        {
            UtilPowerPoint.DeleteBookmarks(_mediaShape, _mediaName);
        }

        /// <summary>
        /// Delete all the shapes 
        /// </summary>
        public void DeleteShapes()
        {
            IList<Shape> captionShapes = CaptionShapes;
            foreach (Shape shape in captionShapes)
            {
                shape.Delete();
            }
        }

        /// <summary>
        /// Aligns all the captions for the selected media object
        /// </summary>
        public void AlignCaptions()
        {
            IList<Shape> captionShapes = CaptionShapes;
            foreach (Shape shape in captionShapes)
            {
                CaptionStyle captionStyle = new CaptionStyle();
                if (Convert.ToBoolean(shape.Tags[Constants.CaptionDisplayArea]))
                {
                    captionStyle.CaptionLines = SetCaptionDisplayAreaLines();
                }

                RectangleF position = Utils.GetShapePosition(_slide, _mediaShape, captionStyle, shape.TextFrame.TextRange.Text);

               
                shape.Left = position.Left;
                shape.Top = position.Top;
                shape.Width = position.Width;
                shape.Height = position.Height;

                StyleCaptionShape(shape, captionStyle);

                var boundHeight = shape.TextFrame.TextRange.BoundHeight;
                if (boundHeight > position.Height)
                {
                    RectangleF mediaRectangle = new RectangleF(_mediaShape.Left, _mediaShape.Top, _mediaShape.Width, _mediaShape.Height);
                    RectangleF slideRectangle = new RectangleF(0, 0, _slide.Application.ActivePresentation.PageSetup.SlideWidth, _slide.Application.ActivePresentation.PageSetup.SlideHeight);

                    var width = Utils.DetermineShapeWidth(captionStyle.CaptionLocation, slideRectangle.Width, mediaRectangle.Width);
                    SizeF textSize = Utils.GetTextboxShapeSizeF(_slide, shape.TextFrame.TextRange.Text, captionStyle, width);

                    var numberLines = Math.Ceiling(boundHeight / textSize.Height);

                    if (numberLines <= captionStyle.CaptionLines)
                    {
                        var loc = Utils.DetermineShapeLocation(slideRectangle, mediaRectangle,
                                                               Utils.GetCaptionLocation(_mediaShape),
                                                               new SizeF(position.Width, boundHeight));
                        shape.Left = loc.X;
                        shape.Top = loc.Y;
                        shape.Height = boundHeight;
                    }
                }
            }
        }

        private static int SetCaptionDisplayAreaLines()
        {
            return 3;
        }


        /// <summary>
        /// Iterate throught the deleted list of captions from the editor then removes them from the slide
        /// </summary>
        /// <param name="captionItemsList">List of deleted captions.</param>
        public void DeleteCaptions(CaptionItemsList captionItemsList)
        {
            foreach (var caption in captionItemsList.CaptionItems)
            {
                RemoveSlideCaptionByInternalId(caption.InternalId);
            }
        }
        public void DeleteCaption(CaptionItem captionItem)
        {
            RemoveSlideCaptionByInternalId(captionItem.InternalId);
        }

        public void SetCaptionsListMediaShapeName(string shapeName, ClosedCaptionsList captionsList)
        {
            string mediaShapeName = Utils.GetShapeName(shapeName);
            captionsList.ClosedCaptions.ToList().ForEach(x => { x.MediaShapeName = mediaShapeName; });
        }
        public void SetCaptionsListMediaShapeName(string shapeName, ClosedCaptionsSortableBindingList captionsList)
        {
            string mediaShapeName = Utils.GetShapeName(shapeName);
            captionsList.ClosedCaptions.ToList().ForEach(x => { x.MediaShapeName = mediaShapeName; });
        }


        /// <summary>
        /// Iterate throught the captions on the slide and delete: Animations, Bookmarks and finally the shape
        /// </summary>
        /// <param name="captionInternalId">Internal Id used for deletion</param>
        private void RemoveSlideCaptionByInternalId(string captionInternalId)
        {
            IList<Shape> captionShapes = CaptionShapes;
            foreach (Shape shape in captionShapes)
            {
                if (shape.Tags[Constants.CaptionInternalId].Equals(captionInternalId))
                {
                    // Animation
                    UtilPowerPoint.DeleteShapeAnimationsByTriggerType(_slide, shape,
                                                                      MsoAnimTriggerType.msoAnimTriggerOnMediaBookmark, new List<string> { shape.Name });

                    // Bookmark
                    UtilPowerPoint.DeleteBookmarkByName(_mediaShape, shape.Tags[Constants.BookMarkEntryName]);
                    UtilPowerPoint.DeleteBookmarkByName(_mediaShape, shape.Tags[Constants.BookMarkExitName]);

                    // Shape
                    shape.Delete();
                }
            }
        }

        /// <summary>
        /// Find a shape by an Id
        /// </summary>
        private Shape FindShapesByInternalId(string internalId)
        {
            Shape foundShape = null;
            IList<Shape> captionShapes = CaptionShapes;
            foreach (Shape shape in captionShapes)
            {
                if (shape.Tags[Constants.CaptionInternalId].Equals(internalId))
                {
                    foundShape = shape;
                }
            }
            return foundShape;
        }

        /// <summary>
        /// Find a shape by specific name
        /// </summary>
        private Shape FindShapesByName(string name)
        {
            Shape foundShape = null;
            IList<Shape> captionShapes = CaptionShapes;
            foreach (Shape shape in captionShapes)
            {
                if (shape.Tags[Constants.CaptionItemSysName].Equals(name))
                {
                    foundShape = shape;
                }
            }
            return foundShape;
        }

        /// <summary>
        /// Update the dirty caption details depending on what was changed.
        /// </summary>
        /// <param name="dirtyItem">Caption item to be updated</param>
        public void UpdateDirtyCaptions(CaptionItem dirtyItem)
        {
            Shape dirtyShape = FindShapesByInternalId(dirtyItem.InternalId);

            if (dirtyShape != null)
            {
                // Update the text part of the item if that has changed
                if (dirtyItem.IsDirtyCaptionText)
                {
                    // string truncateCaptionText = Utils.TruncateCaptionText(_slide, dirtyItem, dirtyShape.Width);
                    // dirtyShape.TextFrame.TextRange.Text = truncateCaptionText;
                    dirtyShape.TextFrame.TextRange.Text = dirtyItem.Text;
                }

                // Delete the caption Animations & bookmarks if the Start or End time was updated
                if (dirtyItem.IsDirtyStartTime || dirtyItem.IsDirtyEndTime)
                {
                    #region delete the bookmarks and animations

                    UtilPowerPoint.DeleteShapeAnimationsByTriggerType(_slide, dirtyShape,
                                                                      MsoAnimTriggerType.
                                                                          msoAnimTriggerOnMediaBookmark, new List<string> { dirtyShape.Name });

                    UtilPowerPoint.DeleteBookmarkByName(_mediaShape, dirtyShape.Tags[Constants.BookMarkEntryName]);
                    UtilPowerPoint.DeleteBookmarkByName(_mediaShape, dirtyShape.Tags[Constants.BookMarkExitName]);

                    #endregion

                    #region recreate the bookmarks and animations

                    AddBookMarks(dirtyItem);

                    //string truncateCaptionText = Utils.TruncateCaptionText(_slide, dirtyItem, dirtyShape.Width);
                    //UtilPowerPoint.SetTextboxCaptionDetails(dirtyShape, dirtyItem, CaptionId, truncateCaptionText);

                    UtilPowerPoint.SetTextboxCaptionDetails(dirtyShape, dirtyItem, CaptionId, dirtyItem.Text);
                    ApplyAnimation(dirtyShape, dirtyItem);

                    #endregion
                }
            }
        }
    }
}
